#pragma once

#include "Thread.hpp"
#include "LockGuard.hpp"

using namespace ThreadNs;

#include <vector>
#include <queue>
#include <iostream>

const int gnum = 3;

template <class T>
class ThreadPool;

template <class T>
class ThreadData
{
public:
    ThreadData(ThreadPool<T> *tp, const std::string &threadname)
        : _threadpool(tp), _threadname(threadname)
    {
    }

    ~ThreadData()
    {
    }

public:
    ThreadPool<T> *_threadpool;
    std::string _threadname;
};

template <class T>
class ThreadPool
{
private:
    static void* handleTask(void* args)
    {
        ThreadData<T>* td = static_cast<ThreadData<T>*>(args);
        while(true)
        {
            T t;
            {
                LockGuard lockguard(td->_threadpool->mutex());
                while(td->_threadpool->isQueueEmpty())
                {
                    td->_threadpool->threadWait();
                }
                t = td->_threadpool->pop();
            }
            std::cout << td->_threadname << " 获取了一个任务： " << t.toTaskString() << " 并处理完成，结果是：" << t() << std::endl;
        }

        delete td;
        return nullptr;
    }
public:
    bool isQueueEmpty() {return _task_queue.empty(); }
    void threadWait() { pthread_cond_wait(&_cond,&_mutex); }
    void lockQueue() {pthread_mutex_lock(&_mutex); }
    void unlockQueue() {pthread_mutex_unlock(&_mutex); }

    T pop()
    {
        T t = _task_queue.front();
        _task_queue.pop();
        return t;
    }
    pthread_mutex_t* mutex() { return &_mutex; }
public:
    ThreadPool(const int &num = gnum)
        : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i < _num; i++)
        {
            _threads.push_back(new Thread());
        }
    }

public:
    void run()
    {
        for (const auto &iter : _threads)
        {
            ThreadData<T> *td = new ThreadData<T>(this, iter->threadname());
            iter->start(handleTask, td);
            std::cout << iter->threadname() << " start..." << std::endl;
        }
    }

    void push(T& in)
    {
        LockGuard lockguard(&_mutex);
        _task_queue.push(in);
        pthread_cond_signal(&_cond);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for (const auto &t : _threads)
        {
            delete t;
        }
    }

private:
    int _num;
    std::vector<Thread *> _threads;
    std::queue<T> _task_queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
};
